package NBT;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.zip.GZIPOutputStream;

import javax.swing.JOptionPane;

public class ChunkEditor {

	MapChunk chunk; //if chink file exists
	File chunk_path; //if chunk file doesn't exist
	
	int alt;
	byte data_block,data_air;
	byte slight_block, slight_air;
	byte blight_block, blight_air;
	byte height;
	
	public ChunkEditor(MapChunk chunk)
	{
		this.chunk=chunk;
		chunk_path=null;
		
		
		alt=0;
		data_block=0;
		data_air=0;
		slight_block=0;
		slight_air=0;
		blight_block=0;
		blight_air=0;
		height=0;
	}
	
	public ChunkEditor(File path)
	{
		chunk=null;
		chunk_path=path;
		

		alt=0;
		data_block=0;
		data_air=0;
		slight_block=0;
		slight_air=0;
		blight_block=0;
		blight_air=0;
		height=0;
	}
	
	public void setChunk(MapChunk chunk)
	{
		this.chunk=chunk;
	}
	
	public void save() throws Exception
	{
		if(chunk==null) return;	
		
		GZIPOutputStream stream=new GZIPOutputStream(new FileOutputStream(chunk.chunkfile));

		chunk.root.save(stream);
		
		stream.close();
	}
	
	public void createBackup()
	{
		if(chunk==null) return;
		
		String destname;
		try {
			destname = chunk.chunkfile.getCanonicalPath();
		} catch (IOException e1) {
			return;
		}
		destname=destname+".BAK";
		File destfile=new File(destname);
		if(destfile.exists())
		{
			if(JOptionPane.showConfirmDialog(null, "Backup already exists for chunk("+chunk.xPos+","+chunk.zPos+")!\nDo you want to overwrite?")!=JOptionPane.YES_OPTION)
			{
				return;
			}
			
			destfile.delete();
		}
		
		FileChannel source=null;
		FileChannel destination=null;
		try{
			source=new FileInputStream(chunk.chunkfile).getChannel();
			destination=new FileOutputStream(destfile).getChannel();
			destination.transferFrom(source, 0, source.size());
		} catch(Exception e) {}
		
		try{
			if(source!=null)
				source.close();
			if(destination!=null)
				destination.close();
		} catch(Exception e){}			
	}
	
	public void restoreBackup()
	{	
		String destname="";	
		File chunkfile=null;
		try {
			if(chunk!=null) 
			{
				destname = chunk.chunkfile.getCanonicalPath();
				chunkfile = chunk.chunkfile;
			}
			else
			{
				destname=chunk_path.getCanonicalPath();
				chunkfile = new File(destname);
			}
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null, "Error: "+e.getMessage());
			return;
		}
		destname=destname+".BAK";
		File backupfile=new File(destname);
		if(!backupfile.exists())
		{
			JOptionPane.showMessageDialog(null, "No backup found for this chunk ("+chunk.xPos+","+chunk.zPos+")! Aborting!");
			return;
		}
		
		FileChannel source=null;
		FileChannel destination=null;
		try{
			source=new FileInputStream(backupfile).getChannel();
			destination=new FileOutputStream(chunkfile).getChannel();
			destination.transferFrom(source, 0, source.size());
		} catch(Exception e) 
		{
			JOptionPane.showMessageDialog(null, "Error: "+e.getMessage());
		}
		
		try{
			if(source!=null)
				source.close();
			if(destination!=null)
				destination.close();
		} catch(Exception e){}			
	}
	
	public void getLevelStatus()
	{
		TAG_Compound level=null;
		TAG_Byte_Array blocks=null,data=null,skylight=null,blocklight=null,heightmap=null;
		
		try {
			level=(TAG_Compound) chunk.root.getElement("Level");
			blocks=(TAG_Byte_Array) level.getElement("Blocks");
			data=(TAG_Byte_Array) level.getElement("Data");
			skylight=(TAG_Byte_Array) level.getElement("SkyLight");
			blocklight=(TAG_Byte_Array) level.getElement("BlockLight");
			heightmap=(TAG_Byte_Array) level.getElement("HeightMap");
		} catch (Exception e)
		{
			JOptionPane.showMessageDialog(null, "Chunk editor error: "+e.getMessage());
			return;
		}
		
		//find first non-air block in middle column
		int pos=128*(8*16+8);
		for(alt=127; alt>=0; alt--)
			if(blocks.data[pos+alt]!=0)
				break;	
		
		//find data for the block and the air above it		
		data_block=getHalfbyte(data.data,pos+alt);
		data_air=getHalfbyte(data.data,pos+alt+1);
		
		//find skylight like above		
		slight_block=getHalfbyte(skylight.data,pos+alt);
		slight_air=getHalfbyte(skylight.data,pos+alt+1);
		
		//find blocklight like before		
		blight_block=getHalfbyte(blocklight.data,pos+alt);
		blight_air=getHalfbyte(blocklight.data,pos+alt+1);
		
		height=heightmap.data[8*16+8];				
	}
	
	public void levelChunk()
	{
		TAG_Compound level=null;
		TAG_Byte_Array blocks=null,data=null,skylight=null,blocklight=null,heightmap=null;
		
		try {
			level=(TAG_Compound) chunk.root.getElement("Level");
			blocks=(TAG_Byte_Array) level.getElement("Blocks");
			data=(TAG_Byte_Array) level.getElement("Data");
			skylight=(TAG_Byte_Array) level.getElement("SkyLight");
			blocklight=(TAG_Byte_Array) level.getElement("BlockLight");
			heightmap=(TAG_Byte_Array) level.getElement("HeightMap");
		} catch (Exception e)
		{
			JOptionPane.showMessageDialog(null, "Chunk editor error: "+e.getMessage());
			return;
		}	
		
		int pos=128*(8*16+8),offset;
		
		//change all the blocks at alt level to grass and air above them
		for(int x=0; x<16; x++)
			for(int z=0; z<16; z++)
			{
				heightmap.data[z*16+x]=height; //its inverted here! (ZX)
								
				for(int y=127; y>alt; y--)
				{
					offset=(x*16+z)*128+y;
					blocks.data[offset]=0;
					setHalfbyte(data.data,offset,data_air);
					setHalfbyte(skylight.data,offset,slight_air);
					setHalfbyte(blocklight.data,offset,blight_air);
				}
				
				offset=(x*16+z)*128+alt;
				blocks.data[offset]=2;
				setHalfbyte(data.data,offset,data_block);
				setHalfbyte(skylight.data,offset,slight_block);
				setHalfbyte(blocklight.data,offset,blight_block);
			}		
	}
	
	public void makeMaze(byte [] maze, int w, int h, int sx, int sz, int dx, int dz)
	{		
		TAG_Compound level=null;
		TAG_Byte_Array blocks=null,data=null,skylight=null,blocklight=null,heightmap=null;
		
		try {
			level=(TAG_Compound) chunk.root.getElement("Level");
			blocks=(TAG_Byte_Array) level.getElement("Blocks");
			data=(TAG_Byte_Array) level.getElement("Data");
			skylight=(TAG_Byte_Array) level.getElement("SkyLight");
			blocklight=(TAG_Byte_Array) level.getElement("BlockLight");
			heightmap=(TAG_Byte_Array) level.getElement("HeightMap");
		} catch (Exception e)
		{
			JOptionPane.showMessageDialog(null, "Chunk editor error: "+e.getMessage());
			return;
		}
		
		int offset;
		
		for(int i=sx,x=dx; i<w && x<16; i++, x++)
			for(int j=sz, z=dz; j<h && z<16; j++, z++)
			{
				if(maze[i*h+j]!=0)
				{
					heightmap.data[z*16+x]-=2; //its inverted here! (ZX)
					for(int k=1; k<=2; k++)
					{
						offset=(x*16+z)*128+alt+k;
						blocks.data[offset]=18;
						setHalfbyte(data.data,offset,data_block);
						setHalfbyte(skylight.data,offset,slight_block);
						setHalfbyte(blocklight.data,offset,blight_block);
					}					
				}
			}
	}
	
	byte getHalfbyte(byte [] array, int pos)
	{
		int off=pos/2;
		int h=pos%2;
		byte val=array[off];
		if(h==0) return (byte) (val&0x0F);
		else return (byte) ((val>>4)&0x0F);
	}
	
	void setHalfbyte(byte [] array, int pos, byte val)
	{
		int off=pos/2;
		int h=pos%2;
		byte x=array[off];
		if(h==0) array[off]=(byte)(x&0xF0|val&0x0F);
		else array[off]=(byte)(x&0x0F|((val&0x0F)<<4));
	}
}
